/**
 * TranslationFactory.java
 *
 * Copyright (C) 2009,  Tomislav Milkovic
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package extjsdyntran.translation;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.fileupload.FileItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * @description Main class for translation services.
 * It is used to register translation services. When translation is in progress,
 * if there is no supported language for current service, next service will be used for translation   
 */ 
public class TranslationFactory  {

	final static Logger logger = LoggerFactory.getLogger(TranslationFactory.class);
	private static ArrayList<TranslationServiceAbstract> services = new ArrayList<TranslationServiceAbstract>();
	
	
	
	public static ArrayList<TranslationServiceAbstract> getServices(){
		return services;
	}
	
	//on initialization add service to factory 
	public static void appendService(String service, String username, String pasword, String path){
		
		if (service==null) {
			logger.warn("Trying to initialize null service");
			return ;
		}
		
		try{
			TranslationServiceAbstract transervice = initService( service,username,pasword, path);
			services.add(transervice);

		}catch (Exception e){
			logger.error("Error initializing service", e);		
		}	
	}
	
	
	//init translation service class; 
	private static TranslationServiceAbstract initService(String service, String username , String password, String path) throws Exception{
		
			Class<?> cl = Class.forName(service);
			Constructor<?> ct = cl.getConstructor(String.class,String.class,String.class);
	        Object retObj = ct.newInstance(username,password,path);
	        			
			return (TranslationServiceAbstract)retObj;
	}
	
	//get first service supporting language for translation
	public static TranslationServiceAbstract getService(String language){
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		while (it.hasNext()){
			service = it.next();
			if (service.isLanguageSupported(language)) {return service;};
		}
		return service;
	}

	//get first service supporting language editing (add,remove languages)
	public static TranslationServiceAbstract getEditableService(String language){
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		while (it.hasNext()){
			service = it.next();
			if (!service.isNewLanguageSupported()) continue;
			return service;
		}
		return service;
	}
	
	//save specific translation insert/update
	public static boolean saveTranslation( String toLanguage, String fromValue, String toValue){
		TranslationServiceAbstract service = getService(toLanguage);
		return service.saveTranslation( toLanguage, fromValue, toValue);
	}
	
	//delete specific translation
	public static boolean deleteTranslation(String fromLanguage, String fromValue){
		TranslationServiceAbstract service = getService(fromLanguage); 
		return service.deleteTranslation(fromLanguage, fromValue);
	}
	
	//save specific language or all languages on all services 
	public static boolean saveLanguages(String language){
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		while (it.hasNext()){
			service = it.next();
			if ("*".equals(language))  {
				logger.info("Saving all language translations to storage!");
				service.saveLanguages(language);
			}else if (service.isLanguageSupported(language)) {
				logger.info("Saving translations for language {} to storage!", language);
				return service.saveLanguages(language);
			};
		}
		return true;
	}
	
	
	
	
	//return number of registered services, if =0 then servlet should stop processing
	public static int size(){
		return services.size();
	}
	

	
	
	private static Collection <String[]> getLanguagesAsJsonArray(Map langs, Collection langList){
		Collection <String[]> result = new ArrayList<String[]>();
        Iterator<String> lngs = langList.iterator();
        while (lngs.hasNext()){
                String key = lngs.next();
                String[] rec = {(String)langs.get(key),key};
                result.add(rec);
        }
        return result;
	}
	
	//retrieve all languages from all registered services
	public static  Collection <String[]>  getLanguages(ArrayList<ServiceTypes> excludes){
		Map langs = new HashMap();
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		while (it.hasNext()){
			service = it.next();
			if(excludes!=null)
				if (excludes.contains(service.getServiceType())) continue;
			Map prop = service.getListOfLanguages();
			langs.putAll(prop);
		}	

		List langList = new ArrayList(langs.keySet()); 
		Collections.sort(langList);	        

        return getLanguagesAsJsonArray(langs,langList);

		
	}
	
	//reload languages in all registered services
	public static void reloadLanguages(){
		logger.info("Reloading all language translations from storage...");
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service;
		while (it.hasNext()){
			service = it.next();
			service.loadLanguages();
		}		
	}
	
	//does a test of every translation service and returns list of failed services
	public static Map<String,Boolean> test(){
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		Map<String,Boolean> response = new HashMap<String,Boolean>();
		while (it.hasNext()){
			service = it.next();
			try {
				boolean test = service.test();
				logger.info("Translation service availability is {} !", test ? "sucessful" : "unsucessful");
				response.put(service.getClass().getCanonicalName(), new Boolean(test));
			} catch (Exception e) {
				response.put(service.getClass().getCanonicalName(), new Boolean(false));
				logger.error("Error testing availability for service {}!", service.getClass().getCanonicalName());
				logger.error("Service test availability error:",e);			
			}			
		}	
		return response;
				
	}
	
	
	
	private static void compressSingleService(ZipOutputStream zout, TranslationServiceAbstract service, String language) throws Exception{

		ByteArrayOutputStream langout = service.compressCache(language);
	    zout.putNextEntry(new ZipEntry(service.getClass().getCanonicalName())); 
	    zout.write(langout.toByteArray(),0,langout.size());
	    zout.closeEntry(); 
	}
	

	public static ByteArrayOutputStream compressCache(String language) throws Exception{
		ByteArrayOutputStream bout=new ByteArrayOutputStream();
		ZipOutputStream zout=new ZipOutputStream(bout);
		
        if(language!=null){
        	TranslationServiceAbstract service = getService(language);
        	compressSingleService(zout,service,language);
        }else{
			Iterator<TranslationServiceAbstract> it =  services.iterator();
			TranslationServiceAbstract service=null;
			while (it.hasNext()){
				service = it.next();
				compressSingleService(zout,service,null);
			}	
        }
		
		zout.finish();
		return bout ; 
	}
	
	private static TranslationServiceAbstract getServiceByClassName(String className){
		
		Iterator<TranslationServiceAbstract> it =  services.iterator();
		TranslationServiceAbstract service=null;
		while (it.hasNext()){
			service = it.next();
			if(className.equals(service.getClass().getCanonicalName())) return service;
		}	
		
		return null; 
	}
	

	public static boolean deCompressCache(FileItem item) throws IOException{
			
        ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(item.get()));
        ZipEntry zEntry = null;      
        
        while( (zEntry = zin.getNextEntry()) != null) {
        	TranslationServiceAbstract service = getServiceByClassName(zEntry.getName());
            ByteArrayOutputStream bos = new ByteArrayOutputStream();           
            int size;
            byte[] buffer = new byte[1024]; 
            while ((size = zin.read(buffer, 0, buffer.length)) != -1) {
         	   bos.write(buffer, 0, size);
         	   }
            
			try {
				service.deCompressCache(bos.toByteArray());
			} catch (Exception e) {
				logger.error("Error decompressing imported translations.",e);
				return false;
			}	
        }
        
        return true;
	}
	
}
